package net.hvacapp.cmvrfforandroid.widget.swipemenulistview;

import android.content.Context;
import android.content.res.TypedArray;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.AbsListView;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;

import com.nineoldandroids.animation.Animator;
import com.nineoldandroids.animation.ObjectAnimator;
import com.nineoldandroids.animation.ValueAnimator;

import net.hvacapp.cmvrfforandroid.R;
import net.hvacapp.cmvrfforandroid.widget.ListViewFooterMore;

/**
 * @author baoyz
 * @date 2014-8-18
 */
public class SwipeMenuListView extends ListView implements AbsListView.OnScrollListener {

    private static final int TOUCH_STATE_NONE = 0;
    private static final int TOUCH_STATE_X = 1;
    private static final int TOUCH_STATE_Y = 2;

    /** 释放刷新 */
    private final int RELEASE_REFRESH = 3;
    /** 下拉刷新 */
    private final int PULL_DOWN_REFRESH = 4;
    /** 刷新中 */
    private final int REFRESHING = 5;
    /** 加载更多 */
    private final int LOADING = 6;
    /** 加载更多延时 */
    private int LOADING_DURATION = 1000;

    private int MAX_Y = 5;
    private int MAX_X = 3;
    private float mDownX;
    private float mDownY;
    private float mLastX;
    private float mLastY;
    private int mTouchState;
    private int mTouchPosition;
    private SwipeMenuLayout mTouchView;
    private OnSwipeListener mOnSwipeListener;

    private SwipeMenuCreator mMenuCreator;
    private OnMenuItemClickListener mOnMenuItemClickListener;
    private Interpolator mCloseInterpolator;
    private Interpolator mOpenInterpolator;

    /** 头部下拉刷新视图 */
    private View mHeaderLayout;
    private ImageView mArrowIv;
    private ProgressBar mRefreshingPb;
    private TextView mPromtTv;

    /** 头部下拉刷新视图高度 */
    private int mHeaderLayoutHeight;
    private int mRefreshState = PULL_DOWN_REFRESH;
    private boolean mFirstItemVisible;
    private OnRefreshListener mOnRefreshListener;

    /** 底部上拉加载更多视图 */
    private ListViewFooterMore mFooterLayout;
    private boolean mLastItemVisible;
    private boolean mIsLoadingMore;

    private int mMode;
    /** 关闭下拉刷新和上拉加载更多 */
    private final int DISABLED = 0x0;
    /** 只开启下拉刷新 */
    private final int PULLFROMSTART = 0x1;
    /** 只开启上拉加载更多 */
    private final int PULLFROMEND = 0x2;
    /** 开启下拉刷新和上拉加载更多 */
    private final int BOTH = 0x3;

    public SwipeMenuListView(Context context) {
        super(context);
        init(context);
        initEvent();
    }

    public SwipeMenuListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context);
        initEvent();
    }

    public SwipeMenuListView(Context context, AttributeSet attrs) {
        super(context, attrs);

        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PullToRefresh);
        if (a.hasValue(R.styleable.PullToRefresh_ptrMode)) {
            mMode = a.getInteger(R.styleable.PullToRefresh_ptrMode, 0);
        }

        init(context);
        initEvent();
    }

    private void init(Context context) {
        switch (mMode) {
            case DISABLED:
                break;
            case PULLFROMSTART:
                initHeaderLayout(context);
                break;
            case PULLFROMEND:
                initFooterLayout(context);
                break;
            case BOTH:
                initHeaderLayout(context);
                initFooterLayout(context);
                break;
        }

        MAX_X = dp2px(MAX_X);
        MAX_Y = dp2px(MAX_Y);
        mTouchState = TOUCH_STATE_NONE;
    }

    private void initEvent() {
        setOnScrollListener(this);
    }

    /**
     * 初始化头布局
     */
    private void initHeaderLayout(Context context) {
        mHeaderLayout = View.inflate(context, R.layout.layout_pull_down_refresh, null);
        measureItem(mHeaderLayout);
        mHeaderLayoutHeight = mHeaderLayout.getMeasuredHeight();
        mHeaderLayout.setPadding(0, -mHeaderLayoutHeight, 0, 0);

        mArrowIv = (ImageView) mHeaderLayout.findViewById(R.id.iv_arrow);
        mRefreshingPb = (ProgressBar) mHeaderLayout.findViewById(R.id.pb_refreshing);
        mPromtTv = (TextView) mHeaderLayout.findViewById(R.id.tv_prompt);

        addHeaderView(mHeaderLayout);
    }

    /**
     * 初始化底部布局
     */
    private void initFooterLayout(Context context) {
        mFooterLayout = new ListViewFooterMore(context);
        mFooterLayout.setListViewFooterStatus(ListViewFooterMore.ListViewFooterStatus.GONE);
        addFooterView(mFooterLayout);
    }

    /**
     * 刷新头视图
     */
    private void refreshHeaderLayout() {
        if (mRefreshState == RELEASE_REFRESH) {
            mArrowIv.setVisibility(View.VISIBLE);
            ObjectAnimator.ofFloat(mArrowIv, "rotation", 0, -180)
                .setDuration(300)
                .start();
            mRefreshingPb.setVisibility(View.INVISIBLE);
            mPromtTv.setText(getResources().getString(R.string.pull_release_label));
        } else if (mRefreshState == PULL_DOWN_REFRESH) {
            mArrowIv.setVisibility(View.VISIBLE);
            ObjectAnimator.ofFloat(mArrowIv, "rotation", -180, 0)
                    .setDuration(300)
                    .start();
            mRefreshingPb.setVisibility(View.INVISIBLE);
            mPromtTv.setText(getResources().getString(R.string.pull_label));
        } else if (mRefreshState == REFRESHING) {
            mArrowIv.setVisibility(View.INVISIBLE);
            mRefreshingPb.setVisibility(View.VISIBLE);
            mPromtTv.setText(getResources().getString(R.string.pull_refreshing_label));
        }
    }

    /**
     * 测量view
     * @param targetView 被测量的view
     */
    private void measureItem(View targetView) {
        ViewGroup.LayoutParams p = targetView.getLayoutParams();
        if (p == null) {
            p = new ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT);
        }

        int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0, p.width);
        int lpHeight = p.height;
        int childHeightSpec;
        if (lpHeight > 0) {
            childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
        } else {
            childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
        }
        targetView.measure(childWidthSpec, childHeightSpec);
    }

    @Override
    public void setAdapter(ListAdapter adapter) {
        super.setAdapter(new SwipeMenuAdapter(getContext(), adapter) {
            @Override
            public void createMenu(SwipeMenu menu) {
                if (mMenuCreator != null) {
                    mMenuCreator.create(menu);
                }
            }

            @Override
            public void onItemClick(SwipeMenuLayout swipeMenuLayout, SwipeMenuView view, SwipeMenu menu,
                                    int index) {
                boolean flag = false;
                if (mOnMenuItemClickListener != null) {
                    flag = mOnMenuItemClickListener.onMenuItemClick(swipeMenuLayout, view.getPosition(), menu, index);
                }
                if (mTouchView != null && !flag) {
                    mTouchView.smoothCloseMenu();
                }
            }
        });
    }

    public void setCloseInterpolator(Interpolator interpolator) {
        mCloseInterpolator = interpolator;
    }

    public void setOpenInterpolator(Interpolator interpolator) {
        mOpenInterpolator = interpolator;
    }

    public Interpolator getOpenInterpolator() {
        return mOpenInterpolator;
    }

    public Interpolator getCloseInterpolator() {
        return mCloseInterpolator;
    }

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            if (msg.what == LOADING) {
                if (mOnRefreshListener != null) {
                    mOnRefreshListener.onPullUpRefresh();
                }
            }
        }
    };

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        if(scrollState == OnScrollListener.SCROLL_STATE_IDLE
                || scrollState == OnScrollListener.SCROLL_STATE_FLING) {
            if (mFooterLayout != null && !mIsLoadingMore && mLastItemVisible && mRefreshState != REFRESHING) {
                mIsLoadingMore = true;
                mFooterLayout.setListViewFooterStatus(ListViewFooterMore.ListViewFooterStatus.LOADING);
                this.setSelection(this.getCount());

                handler.sendEmptyMessageDelayed(LOADING, LOADING_DURATION);
            }
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        if (firstVisibleItem == 0) {
            mFirstItemVisible = true;
        } else {
            mFirstItemVisible = false;
        }

        mLastItemVisible = (totalItemCount > 0 && firstVisibleItem + visibleItemCount >= totalItemCount);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {

          //在数据较少或没有时，在空白区域做下拉操作，DOWN和UP事件被注释内容处理掉了，无法实现下拉操作，因此此处被注释了。
//        if (ev.getAction() != MotionEvent.ACTION_DOWN && mTouchView == null)
//            return super.onTouchEvent(ev);
//        int action = MotionEventCompat.getActionMasked(ev);
        int action = ev.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                int oldPos = mTouchPosition;
                mDownX = ev.getX();
                mDownY = ev.getY();
                mTouchState = TOUCH_STATE_NONE;

                mTouchPosition = pointToPosition((int) ev.getX(), (int) ev.getY());

                if (mTouchPosition == oldPos && mTouchView != null
                        && mTouchView.isOpen()) {
                    mTouchState = TOUCH_STATE_X;
                    mTouchView.onSwipe(ev);
                    return true;
                }

                View view = getChildAt(mTouchPosition - getFirstVisiblePosition());

                if (mTouchView != null && mTouchView.isOpen()) {
                    mTouchView.smoothCloseMenu();
                    mTouchView = null;
                    return super.onTouchEvent(ev);
                }
                if (view instanceof SwipeMenuLayout) {
                    mTouchView = (SwipeMenuLayout) view;
                }
                if (mTouchView != null) {
                    mTouchView.onSwipe(ev);
                }
                break;
            case MotionEvent.ACTION_MOVE:
                mLastX = ev.getX();
                mLastY = ev.getY();
                float distanceY = mLastY - mDownY;
                float distanceX = mLastX - mDownX;
                float dy = Math.abs(distanceY);
                float dx = Math.abs(distanceX);
                if (dx > dy) {
                    if (mTouchState == TOUCH_STATE_X) {
                        if (mTouchView != null) {
                            mTouchView.onSwipe(ev);
                        }
                        getSelector().setState(new int[]{0});
                        ev.setAction(MotionEvent.ACTION_CANCEL);
                        super.onTouchEvent(ev);
                        return true;
                    } else if (mTouchState == TOUCH_STATE_NONE) {
                        if (Math.abs(dy) > MAX_Y) {
                            mTouchState = TOUCH_STATE_Y;
                        } else if (dx > MAX_X) {
                            mTouchState = TOUCH_STATE_X;
                            if (mOnSwipeListener != null) {
                                mOnSwipeListener.onSwipeStart(mTouchPosition);
                            }
                        }
                    }
                } else {
                    if (mHeaderLayout != null) {
                        int paddingTop = (int) ((-mHeaderLayoutHeight + distanceY * 0.5));
                        if (!mIsLoadingMore && mFirstItemVisible && mRefreshState != REFRESHING && paddingTop > -mHeaderLayoutHeight) {
                            if (paddingTop > 0 && mRefreshState == PULL_DOWN_REFRESH) {
                                mRefreshState = RELEASE_REFRESH;
                                refreshHeaderLayout();
                            }
                            if (paddingTop < 0 && mRefreshState == RELEASE_REFRESH) {
                                mRefreshState = PULL_DOWN_REFRESH;
                                refreshHeaderLayout();
                            }
                            mHeaderLayout.setPadding(0, paddingTop, 0, 0);

                            if (paddingTop > 0) {
                                return true;
                            }
                        }
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
                if (mHeaderLayout != null) {
                    mLastX = ev.getX();
                    mLastY = ev.getY();
                    float tempDx = Math.abs(mLastX - mDownX);
                    float tempDy = Math.abs(mLastY - mDownY);
                    if (tempDy > 3 && tempDy > tempDx) {
                        if (mRefreshState == RELEASE_REFRESH) {
                            mRefreshState = REFRESHING;
                            refreshHeaderLayout();
                            mHeaderLayout.setPadding(0, 0, 0, 0);

                            if (mOnRefreshListener != null) {
                                mOnRefreshListener.onPullDownRefresh();
                            }
                        } else if (mRefreshState == PULL_DOWN_REFRESH && 0 < (mLastY - mDownY)) {
                            mHeaderLayout.setPadding(0, -mHeaderLayoutHeight, 0, 0);
                        }
                        return super.onTouchEvent(ev);
                    }
                }

                if (mTouchState == TOUCH_STATE_X) {
                    if (mTouchView != null) {
                        mTouchView.onSwipe(ev);
                        if (!mTouchView.isOpen()) {
                            mTouchPosition = -1;
                            mTouchView = null;
                        }
                    }
                    if (mOnSwipeListener != null) {
                        mOnSwipeListener.onSwipeEnd(mTouchPosition);
                    }
                    ev.setAction(MotionEvent.ACTION_CANCEL);
                    super.onTouchEvent(ev);
                    return true;
                }
                break;
        }
        return super.onTouchEvent(ev);
    }

    public void onRefreshComplete() {
        if (mIsLoadingMore) {
            mIsLoadingMore = false;
            mFooterLayout.setListViewFooterStatus(ListViewFooterMore.ListViewFooterStatus.GONE);
        } else {
            if (mRefreshState == REFRESHING) {
                HeaderBackAndUpdate();
            }
        }
    }

    /**
     * 已一个加速动画隐藏头部视图,并更新头部视图
     */
    private void HeaderBackAndUpdate() {
        ValueAnimator springbackAnim = ValueAnimator.ofInt(0, -mHeaderLayoutHeight);
        springbackAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int value = (int) animation.getAnimatedValue();
                mHeaderLayout.setPadding(0, value, 0, 0);
            }
        });
        springbackAnim.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {

            }

            @Override
            public void onAnimationEnd(Animator animation) {
                mRefreshState = PULL_DOWN_REFRESH;
                mRefreshingPb.setVisibility(View.INVISIBLE);
                mArrowIv.setVisibility(View.VISIBLE);
                mPromtTv.setText(getResources().getString(R.string.pull_label));
            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
        springbackAnim.setDuration(200);
        springbackAnim.setInterpolator(new AccelerateInterpolator());
        springbackAnim.start();
    }

    public void smoothOpenMenu(int position) {
        if (position >= getFirstVisiblePosition()
                && position <= getLastVisiblePosition()) {
            View view = getChildAt(position - getFirstVisiblePosition());
            if (view instanceof SwipeMenuLayout) {
                mTouchPosition = position;
                if (mTouchView != null && mTouchView.isOpen()) {
                    mTouchView.smoothCloseMenu();
                }
                mTouchView = (SwipeMenuLayout) view;
                mTouchView.smoothOpenMenu();
            }
        }
    }

    private int dp2px(int dp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
                getContext().getResources().getDisplayMetrics());
    }

    public void setMenuCreator(SwipeMenuCreator menuCreator) {
        this.mMenuCreator = menuCreator;
    }

    public void setOnMenuItemClickListener(
            OnMenuItemClickListener onMenuItemClickListener) {
        this.mOnMenuItemClickListener = onMenuItemClickListener;
    }

    public void setOnSwipeListener(OnSwipeListener onSwipeListener) {
        this.mOnSwipeListener = onSwipeListener;
    }

    public static interface OnMenuItemClickListener {
        boolean onMenuItemClick(SwipeMenuLayout swipeMenuLayout, int position, SwipeMenu menu, int index);
    }

    public void setOnRefreshListener(OnRefreshListener mOnRefreshListener) {
        this.mOnRefreshListener = mOnRefreshListener;
    }

    public static interface OnSwipeListener {
        void onSwipeStart(int position);

        void onSwipeEnd(int position);
    }

}
